﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyX.DSL
{

    /// <summary>
    /// 提供属性映射功能
    /// </summary>
    public class XMapper
    {
        public XMapper(object source, object target)
        {
            _a = new XMapperObject(source);
            _b = new XMapperObject(target);

            _numberRules = new List<XMapperNumberMoreThanZeroRule>();
        }

        /// <summary>
        /// 设置源对象中不会被取值的属性
        /// </summary>
        /// <param name="names"></param>
        public void RemoveSourceProperty(params string[] names)
        {
            _a.AddRemoveRule(names);
        }

        /// <summary>
        /// 设置目标对象中不会被赋值的属性
        /// </summary>
        /// <param name="names"></param>
        public void RemoveTargetProperty(params string[] names)
        {
            _b.AddRemoveRule(names);
        }

        /// <summary>
        /// 设置属性的值必须大于0的规则
        /// </summary>
        /// <param name="names"></param>
        public void AddNumberFilter(params string[] names)
        {
            if (names != null)
                if (names.Length != 0)
                {
                    foreach (var name in names)
                    {
                        if (!string.IsNullOrWhiteSpace(name))
                            _numberRules.Add(new XMapperNumberMoreThanZeroRule(name));
                    }
                }
        }

        /// <summary>
        /// 将源对象中有值的属性，赋值到目标对象的属性。
        /// <para>请注意，id并不会被赋值，如果有需要，请手动赋值</para>        
        /// </summary>
        public void Map()
        {
            //取出对象a中有值的属性的名字
            var names = _a.Names;
            //再筛选出通过a,b两个对象规则的属性名
            names = XMapperFilter.Filter(names, _a.NameRules);
            names = XMapperFilter.Filter(names, _b.NameRules);

            var aType = _a.Source.GetType();
            var bType = _b.Source.GetType();

            foreach (var name in names)
            {
                if (name.ToLower() == "id")
                    continue;

                var aProperty = aType.GetProperty(name);
                var bProperty = bType.GetProperty(name);

                if (bProperty == null)
                    continue;

                var aValue = aProperty.GetValue(_a.Source);

                if (!XMapperFilter.Pass(name, aValue, _numberRules))
                    continue;

                bProperty.SetValue(_b.Source, aValue);
            }
        }

        private XMapperObject _a;
        private XMapperObject _b;

        private List<XMapperNumberMoreThanZeroRule> _numberRules;
    }

    public class XMapperFilter
    {
        public static List<string> Filter(List<string> values, List<XMapperNameNotExitsRule> rules)
        {
            var result = new List<string>();

            foreach (var value in values)
            {
                if (Pass(value, rules))
                    result.Add(value);
            }

            return result;
        }

        public static bool Pass(string value, List<XMapperNameNotExitsRule> rules)
        {
            foreach (var rule in rules)
            {
                if (!rule.Pass(value))
                    return false;
            }
            return true;
        }

        public static bool Pass(string name, object value, List<XMapperNumberMoreThanZeroRule> rules)
        {
            foreach (var rule in rules)
            {
                if (rule.Name.ToLower() == name.ToLower())
                    return rule.Pass(value);
            }
            return true;
        }

    }

    public class XMapperObject
    {

        public XMapperObject(object value)
        {
            Source = value;
            NameRules = new List<XMapperNameNotExitsRule>();
        }


        /// <summary>
        /// 所有值不为空的属性名
        /// </summary>
        public List<string> Names
        {
            get
            {
                var result = new List<string>();
                var type = Source.GetType();
                var properties = type.GetProperties();
                foreach (var property in properties)
                {
                    var propertyValue = property.GetValue(Source);
                    if (HasValue(propertyValue))
                        result.Add(property.Name);
                }

                return result;
            }
        }

        public void AddRemoveRule(params string[] names)
        {
            if (names != null)
                if (names.Length != 0)
                {
                    foreach (var name in names)
                    {
                        if (!string.IsNullOrWhiteSpace(name))
                            NameRules.Add(new XMapperNameNotExitsRule(name));
                    }
                }
        }


        public object Source { get; private set; }


        public List<XMapperNameNotExitsRule> NameRules { get; private set; }

        /// <summary>
        /// 判断该值是否真正有值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private bool HasValue(object value)
        {
            if (value == null)
                return false;
            if (string.IsNullOrWhiteSpace(value.ToString()))
                return false;
            double number = 0;
            if (double.TryParse(value.ToString(), out number))
                if (number == 0)
                    return false;
            return true;
        }
    }

    /// <summary>
    /// 对象属性映射规则
    /// </summary>
    public interface XIMapperRule
    {
        bool Pass(object value);
    }

    public class XMapperNameNotExitsRule : XIMapperRule
    {
        public XMapperNameNotExitsRule(string name)
        {
            _name = name;
        }

        public bool Pass(object value)
        {
            if (value == null)
                return false;
            return _name.ToLower() != value.ToString().ToLower();
        }

        private string _name;
    }


    public class XMapperNumberMoreThanZeroRule : XIMapperRule
    {
        public XMapperNumberMoreThanZeroRule(string name)
        {
            Name = name;
        }

        public bool Pass(object value)
        {
            try
            {
                if (value == null)
                    return false;

                var number = int.Parse(value.ToString());
                if (number > 0)
                    return true;
                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public string Name { get; private set; }
    }
}
